#!/usr/bin/env python
# -*- encoding: utf-8 -*-
#
# Based on pygame's "makeref.py".

import os, glob

FROM_DIRECTORIES = ["../pygame_trunk/lib/", "../pygame_trunk/src"]
TO_DIRECTORY = "../reference/source/api/"
ENABLE_COMMENTS = False


def write_string_with_underline(file, string, character='='):
    file.write("%s\n" %string)
    file.write("%s\n\n" %(character * len(string)))

def sortkey(x):
    return os.path.basename(x).lower()

def sort_list_by_keyfunc(alist, akey):
    """ sort(key=sortkey) is only in python2.4.
         this is not inplace like list.sort()
    """
    # make a list of tupples with the key as the first.
    keys_and_list = zip(map(akey, alist), alist)
    keys_and_list.sort()
    alist = map(lambda x:x[1], keys_and_list)
    return alist

def collect_doc_files():
    this_dir = os.path.abspath(os.path.dirname(__file__))
    
    # Obtiene todos los archivos .doc
    files = [glob.glob(os.path.join(this_dir, p, '*.doc')) 
                for p in FROM_DIRECTORIES]

    files = reduce(lambda x,y:x+y, files)
    files = sort_list_by_keyfunc(files, sortkey)

    return files

def run():
    # Obtiene todos los nombres de archivos .doc
    files = collect_doc_files()
    
    # Almacena tuplas de objetos Doc.
    docs = []
    pages = []

    for f in files:
        name = os.path.splitext(os.path.basename(f))[0]
        pages.append(name)
        d = name, Doc('', open(f, "U"))

        # Evita documentos vacios
        if d[1].name:
            docs.append(d)
    
    pages = sort_list_by_keyfunc(pages, str.lower)

    pages.insert(0, "index")
    
    # Tiene un diccionario enlazando nombres de funciones y archivos.
    # (ver la función 'MakeIndex').
    index = {}
    justDocs = []

    for name, doc in docs:
        justDocs.append(doc)
        MakeIndex(name, doc, index)
    
    # Genera uno a uno los archivos .rst
    for name, doc in docs:
        fullname = os.path.join(TO_DIRECTORY, "%s.rst" % name)
        print "making:", fullname
        outFile = open(fullname, "w")
        write_output_file(doc, index, outFile)
        outFile.close()
 
    outFile = open(os.path.join("pygamedocs.h"), "w")
    outFile.write("/* Auto generated file: with makeref.py .  Docs go in src/ *.doc . */\n")
    for doc in justDocs:
        WriteDocHeader(outFile, doc)


    outFile.write("\n\n/* Docs in a comments... slightly easier to read. */\n\n\n/*")
    # add the docs as comments to the header file.
    for doc in justDocs:
        WriteDocHeaderComments(outFile, doc)

    outFile.write("\n\n*/\n\n")


    topDoc = LayoutDocs(justDocs)

    #outFile = open("index.html", "w")
    #outFile.write(HTMLHeader % "Index")
    #WritePageLinks(outFile, pages)
    #outFile.write(HTMLMid)
    #outFile.write("<ul>\n\n")
    #WriteIndex(outFile, index, topDoc)
    #outFile.write("\n\n</ul>\n")
    #outFile.write(HTMLFinish)
    #outFile.close()


def write_output_file(doc, index, f, do_tab=True):

    if doc.kids:
        write_string_with_underline(f, doc.fullname, "#")
        f.write(".. module:: %s\n" %(doc.fullname))

        if doc.descr:
            f.write("    :synopsis: %s\n\n" % doc.descr) 

        do_tab = False
    else:
        pass
        #write_string_with_underline(f, doc.fullname, "-")



    if doc.protos:
        for p in doc.protos:
            f.write(".. function:: %s \n\n" % p)

            if doc.descr:
                f.write("    %s\n\n" % doc.descr) 

            do_tab = True

    if doc.docs:
        pre = False
        for d in doc.docs:
            if d[0] == '*':
                # comienza una lista
                f.write("\n")

                for li in d[1:].split('*'):
                    txt = HtmlPrettyWord(li)
                    f.write(" * %s\n" % txt)

                f.write("\n")
            else:
                txt, pre = HtmlPrettyLine(d, index, pre, do_tab)
                f.write(txt)
        if pre:
            f.write("\n")
    else:
        f.write(" \n")

    if ENABLE_COMMENTS:
        f.write("<!--COMMENTS:"+doc.fullname+"-->")
        f.write("\n")
    
    if doc.kids:
        for k in doc.kids:
            write_output_file(k, index, f)



def HtmlPrettyWord(word):
    if "." in word[:-1] or word.isupper():
        return "*%s*" % word
    return word



def HtmlPrettyLine(line, index, pre, do_tab):
    starts_empty = line[0].isspace() and line[1].isspace()
    pretty = ""
    
    if starts_empty:
        if not pre:
            if do_tab:
                pretty += "    ::\n\n"
            else:
                pretty += "::\n\n"

            pre = True
        else:
            if do_tab:
                pretty += "    "

    elif pre:
        pre = False
        pretty += "\n"
    
    if not pre:
        if do_tab:
            pretty += "    "

            if pre:
                pretty += "    "

        for word in line.split():
            if word[-1] in ",.":
                finish = word[-1]
                word = word[:-1]
            else:
                finish = ""


            link = index.get(word)

            if link:
                pretty += " :func:`%s` " %(word[:-2])

            elif word.isupper() or "." in word[1:-1]:
                pretty += "``%s%s`` " % (word, finish)
            else:
                pretty += "%s%s " % (word, finish)

        pretty += "    \n\n"
    else:
        pretty += line + "\n"
    return pretty, pre



def MakeIndex(name, doc, index):
    """Genera los elementos de un diccionario auxiliar de vínculos.

    Estos elementos de diccionario se almacenan en el argumento 'index' y
    luego se utiliza para construir las referencias de las páginas.

    Una vez que se llama a la función, el diccionario se verá algo así:

        >> print index
        {
            'pygame.draw.polygon()': '<a href="draw.html#pygame.draw.poly...",
            'pygame.display.flip()': '<a href...",
        }
    """
        
    if doc.fullname:
        link = '<a href="%s.html#%s">%s</a> - <font size=-1>%s</font>' % (name, doc.fullname, doc.fullname, doc.descr)
        index[doc.fullname + "()"] = link
    if doc.kids:
        for kid in doc.kids:
            MakeIndex(name, kid, index)

def LayoutDocs(docs):
    levels = {}
    for doc in docs:
        if doc.fullname:
            topName = doc.fullname.split(".")[-1]
            levels[topName] = doc

    if levels.has_key('pygame'):
        top = levels["pygame"]
    else:
        top = None

    for doc in docs:
        if doc is top:
            continue

        parentName = doc.fullname.split(".")[-2]

        parent = levels.get(parentName)
        if parent is not None:
            parent.kids.append(doc)

    return top




def WriteDocHeader(f, doc):
    name = doc.fullname.replace(".", "")
    name = name.replace("_", "")
    name = name.upper()
    defineName = "DOC_" + name
    text = ""
    if doc.protos:
        text = "\\n".join(doc.protos)
    if doc.descr:
        if text:
            text += "\\n"
        text += doc.descr
    
    f.write('#define %s "%s"\n\n' % (defineName, text))

    if doc.kids:
        for kid in doc.kids:
            WriteDocHeader(f, kid)

def WriteDocHeaderComments(f, doc):
    name = doc.fullname

    defineName = name
    text = ""
    if doc.protos:
        text = "\n".join(doc.protos)
    if doc.descr:
        if text:
            text += "\n"
        text += doc.descr
    text = text.replace("\\n", "\n")
    f.write('\n\n%s\n %s\n\n' % (defineName, text))

    if doc.kids:
        for kid in doc.kids:
            WriteDocHeaderComments(f, kid)






class Doc(object):
    """Representa el contenido de un documento completo."""

    def __init__(self, parentname, f):
        self.kids = None
        self.protos = []
        self.docs = None
        self.descr = ""
        self.name = ""
        self.fullname = ""
        self.finished = False

        curdocline = ""
        while True:
            line = f.readline()
            if not line:
                break
            line = line.rstrip()
            line = line.replace('*', '\*')

            if line == "<END>":
                if curdocline:
                    self.docs.append(curdocline)
                    curdocline = ""
                self.finished = True
                break

            if self.kids is not None:
                kid = Doc(self.fullname, f)
                if kid:
                    self.kids.append(kid)


            if line == "<SECTION>":
                if curdocline:
                    self.docs.append(curdocline)
                    curdocline = ""
                self.kids = []
                continue
            
            if line:
                if self.docs is not None:
                    if line[0].isspace():
                        if curdocline:
                            self.docs.append(curdocline)
                            curdocline = ""
                        self.docs.append(line)
                    else:
                        curdocline += line + " "
                elif not self.name:
                    self.name = line
                    if len(line) > 1 and line[0] == '"' and line[-1] == '"':
                        self.fullname = line[1:-1]
                    elif parentname:
                        splitparent = parentname.split(".")
                        if splitparent[-1][0].isupper():
                            self.fullname = splitparent[-1] + "." + line
                        else:
                            self.fullname = parentname + "." + line
                    else:
                        self.fullname = line
                elif not self.descr:
                    self.descr = line
                else:
                    line = line.replace(": return", " -> ")
                    line = line.replace(":", "")
                    self.protos.append(line)
            else:
                if self.docs is not None:
                    if curdocline:
                        self.docs.append(curdocline)
                    curdocline = ""
                elif self.name and self.kids is  None:
                    self.docs = []


    def __repr__(self):
        return "<Doc '%s'>" % self.name
            
    def __nonzero__(self):
        return self.finished

    def __cmp__(self, b):
        return cmp(self.name.lower(), b.name.lower())

def docs_as_dict():
    """

    Dict Format:

        {'pygame.rect.Rect.center': 'Rect.center: ...' ...}

    Generally works, has some workarounds, inspect results manually.

    """

    import pygame
    files = collect_doc_files()

    def make_mapping(doc, parent_name):
        docs = {}
        for k in doc.kids:
            if k.docs:
                kid_name = k.fullname

                if parent_name == 'pygame':
                    if hasattr(pygame.base, k.name):
                        kid_name = '%s.%s' % ('pygame.base', k.name)

                elif not kid_name.startswith(parent_name):
                    kid_name = '%s.%s' % (parent_name, kid_name)

                docs[kid_name] = '\n'.join(k.docs)

            if k.kids:
                docs.update(make_mapping(k, parent_name))
        return docs

    mapping = {}
    for f in files:
        doc = Doc('', open(f, "U"))
        mapping.update(make_mapping(doc, doc.name.lower()))

    return mapping

if __name__ == '__main__':
    run()
